/*
 * FeedClient.java
 *
 * Created on April 12, 2007, 9:04 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app.client;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import org.infoset.xml.Document;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentDestination;
import org.infoset.xml.util.XMLWriter;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;

/**
 *
 * @author alex
 */
public class FeedClient extends AppClient
{

   Context context;
   Client locClient;
   Client editClient;
   Feed feed;
   URI location;
   URI editURI;
   
   /** Creates a new instance of FeedClient */
   public FeedClient(Reference location)
   {
      this(URI.create(location.toString()));
   }
   
   /** Creates a new instance of FeedClient */
   public FeedClient(URI location)
   {
      this(null,location,location);
   }
   /** Creates a new instance of FeedClient */
   public FeedClient(Client client,Reference location)
   {
      this(client,URI.create(location.toString()),URI.create(location.toString()));
   }
   public FeedClient(Client client,Reference location,Reference editURI)
   {
      this(client,URI.create(location.toString()),URI.create(editURI.toString()));
   }
   public FeedClient(Client client,URI location,URI editURI)
   {
      this.context = new Context(Logger.getLogger(EntryClient.class.getName()));
      this.feed = null;
      this.location = location;
      this.editURI = editURI;
      this.locClient = client==null ? new Client(context,Protocol.valueOf(location.getScheme())) : client;
      locClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
      this.editClient = client==null ? new Client(context,Protocol.valueOf(editURI.getScheme())) : client;
      editClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
   }
   
   public FeedClient(Reference location,Identity identity)
   {
      this(URI.create(location.toString()),identity);
   }
   
   /** Creates a new instance of FeedClient */
   public FeedClient(URI location,Identity identity)
   {
      this(null,location,location,identity);
   }
   /** Creates a new instance of FeedClient */
   public FeedClient(Client client,URI location,URI editURI,Identity identity)
   {
      super(identity);
      this.context = new Context(Logger.getLogger(EntryClient.class.getName()));
      this.feed = null;
      this.location = location;
      this.editURI = editURI;
      this.locClient = client==null ? new Client(context,Protocol.valueOf(location.getScheme())) : client;
      locClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
      this.editClient = client==null ? new Client(context,Protocol.valueOf(editURI.getScheme())) : client;
      editClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
   }
   
   public FeedClient(Feed feed,Identity identity)
   {
      super(identity);
      this.context = new Context(Logger.getLogger(EntryClient.class.getName()));
      this.feed = feed;
      this.location = feed.getDocument().getBaseURI();
      List<Link> editLinks = feed.getLinks().get("edit");
      if (editLinks==null || editLinks.size()==0) {
         this.editURI = this.location;
      } else {
         this.editURI = editLinks.get(0).getLink();
      }
      if (this.editURI==null) {
         throw new IllegalArgumentException("The feed instance does not have an edit URI or a base URI.");
      }
      this.locClient = new Client(context,Protocol.valueOf(location.getScheme()));
      locClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
      this.editClient = new Client(context,Protocol.valueOf(editURI.getScheme()));
      editClient.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);

   }
   public EntryClient getEntryClient(Entry entry)
   {
      return new EntryClient(editClient,entry,identity);
   }
   
   public URI getLocation() {
      return location;
   }
   
   public URI getEditURI() {
      return editURI;
   }
   
   public boolean exists() {
      Request request = new Request(Method.HEAD,location.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      Response response = locClient.handle(request);
      if (response.isEntityAvailable()) {
         response.getEntity().release();
      }
      return response.getStatus().isSuccess();
   }
   
   public Status delete() {
      Request request = new Request(Method.DELETE,location.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      Response response = locClient.handle(request);
      return response.getStatus();
   }
   
   public Response get() {
      Request request = new Request(Method.GET,location.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      return locClient.handle(request);
   }
   
   public static void process(Response response,FeedDestination dest)
      throws IOException,XMLException
   {
      XMLRepresentationParser parser = new XMLRepresentationParser();
      parser.parse(response, dest);
   }
   
   public Response get(FeedDestination dest)
      throws IOException,XMLException
   {
      Response response = get();
      try {
         if (response.getStatus().isSuccess()) {
            process(response,dest);
         }
      } finally {
         if (response.getEntity()!=null) {
            response.getEntity().release();
         }
      }
      return response;
   }
   
   public Status create(Feed feed)
   {
      return create(feed.getDocument());
   }
   
   public Status create(Document feedDoc)
   {
      StringWriter sw = new StringWriter();
      try {
         XMLWriter.writeDocument(feedDoc,sw);
      } catch (IOException ex) {
         // should never happen
      } catch (XMLException ex) {
         // should never happen
      }
      return create(sw.toString());
   }
   public Status create(String xml)
   {
      Request request = new Request(Method.POST,location.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(new StringRepresentation(xml,MediaType.APPLICATION_ATOM_XML));
      Response response = locClient.handle(request);
      if (response.isEntityAvailable()) {
         response.getEntity().release();
      }
      return response.getStatus();
   }
   
   public Status update()
   {
      if (feed==null) {
         throw new IllegalStateException("The feed client does not have a feed instance to update.");
      }  
      return update(feed.getDocument());
   }

   public Status update(Feed theFeed)
   {
      return update(theFeed.getDocument());
   }

   public Status update(Document feedDoc)
   {
      StringWriter sw = new StringWriter();
      try {
         XMLWriter.writeDocument(feedDoc,sw);
      } catch (IOException ex) {
         // should never happen
      } catch (XMLException ex) {
         // should never happen
      }
      return update(sw.toString());
   }
   public Status update(String xml)
   {
      Request request = new Request(Method.PUT,editURI.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(new StringRepresentation(xml,MediaType.APPLICATION_ATOM_XML));
      Response response = editClient.handle(request);
      if (response.isEntityAvailable()) {
         response.getEntity().release();
      }
      return response.getStatus();
   }
   
   public URI getEntryLocation(UUID entryId)
   {
      return location.resolve("./_/"+entryId.toString());
   }
   
   public Status deleteEntry(UUID entryId)
   {
      URI entryLocation = getEntryLocation(entryId);
      Request request = new Request(Method.DELETE,entryLocation.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      Response response = locClient.handle(request);
      return response.getStatus();
   }
   
   public Entry createEntry(Entry entry)
      throws StatusException,IOException,XMLException
   {
      StringWriter sw = new StringWriter();
      XMLWriter.writeDocument(entry.getDocument(),sw);
      return createEntry(sw.toString());
   }
   
   public Entry createEntry(Document entryDoc)
      throws StatusException,IOException,XMLException
   {
      StringWriter sw = new StringWriter();
      XMLWriter.writeDocument(entryDoc,sw);
      return createEntry(sw.toString());
   }
   
   public Entry createEntry(String xml)
      throws StatusException,IOException,XMLException
   {
      Request request = new Request(Method.POST,editURI.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(new StringRepresentation(xml,MediaType.APPLICATION_ATOM_XML));
      Response response = new Response(request);
      editClient.handle(request,response);
      Status status = response.getStatus();
      if (response.isEntityAvailable()) {
         response.getEntity().release();
      }
      if (!status.isSuccess()) {
         throw new StatusException("Cannot create entry due to status "+status.getCode(),status);
      }
      Reference location = response.getLocationRef();
      
      request = new Request(Method.GET,location);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      Response entryResponse = locClient.handle(request);
      if (!entryResponse.getStatus().isSuccess()) {
         if (entryResponse.isEntityAvailable()) {
            entryResponse.getEntity().release();
         }
         throw new StatusException("Cannot retrieve new entry from "+location,entryResponse.getStatus());
      }
      DocumentDestination docDest = new DocumentDestination();
      XMLRepresentationParser parser = new XMLRepresentationParser();
      parser.parse(entryResponse,docDest);
      Entry newEntry = new Entry(docDest.getDocument());
      newEntry.index();
      entryResponse.getEntity().release();
      return newEntry;
   }
   
   public Status updateEntry(UUID entryId,Document entryDoc)
   {
      StringWriter sw = new StringWriter();
      try {
         XMLWriter.writeDocument(entryDoc,sw);
      } catch (IOException ex) {
         // should never happen
      } catch (XMLException ex) {
         // should never happen
      }
      return updateEntry(entryId,sw.toString());
   }
   public Status updateEntry(UUID entryId,String xml)
   {
      URI entryURI = getEntryLocation(entryId);
      Request request = new Request(Method.PUT,entryURI.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(new StringRepresentation(xml,MediaType.APPLICATION_ATOM_XML));
      Response response = editClient.handle(request);
      return response.getStatus();
   }
   
   public Entry createMedia(String filePath,Representation rep)
      throws StatusException,IOException,XMLException
   {
      return createMedia(null,filePath,rep);
   }
   
   public Entry createMedia(UUID id,String filePath,Representation rep)
      throws StatusException,IOException,XMLException
   {
      Request request = new Request(Method.POST,editURI.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      Form headers = (Form)request.getAttributes().get("org.restlet.http.headers");
      if (headers==null) {
         headers = new Form();
         request.getAttributes().put("org.restlet.http.headers",headers);
      }
      if (filePath!=null) {
         headers.set("slug",filePath,true);
      }
      if (id!=null) {
         headers.set("id",id.toString(),true);
      }
      request.setEntity(rep);
      Response entryResponse = editClient.handle(request);
      if (!entryResponse.getStatus().isSuccess()) {
         throw new StatusException("Cannot retrieve new entry from "+location,entryResponse.getStatus());
      }
      DocumentDestination docDest = new DocumentDestination();
      XMLRepresentationParser parser = new XMLRepresentationParser();
      parser.parse(entryResponse,docDest);
      Entry newEntry = new Entry(docDest.getDocument());
      newEntry.index();
      return newEntry;
   }
   
   public Status updateMedia(String filePath,Representation rep)
   {
      URI mediaURI = location.resolve("./"+filePath);
      Request request = new Request(Method.PUT,mediaURI.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(rep);
      Response response = editClient.handle(request);
      return response.getStatus();
   }
   
}
